/*
* ECARX Technology Limited is the owner of the copyright and the trade secret of this software.
* Without permission, no one has the right to obtain, disclose or use this software in any way.
*/

package ecarx.xsf.notificationcenter;

import android.app.Notification;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Message;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;

import androidx.annotation.RequiresApi;

import android.text.TextUtils;
import android.util.Log;

import ecarx.xsf.notificationcenter.binders.LauncherControllerProxy;
import ecarx.xsf.notificationcenter.binders.SceneCardProxy;

import ecarx.xsf.notificationcenter.utils.AppUtils;

import java.util.Arrays;
import java.util.HashSet;
import java.util.concurrent.atomic.AtomicBoolean;

import ecarx.xsf.notificationcenter.notification.INotificationListener;
import ecarx.xsf.notificationcenter.notification.INotificationService;


/**
 * 通知中心核心服务
 */
public class ECarxNotificationListenerService extends NotificationListenerService {

    private static final String TAG = "NotificationService";
    private static final boolean DEBUG = false;
    private static final String CHANNEL_PREFIX = "com.android.server.wm.AlertWindowNotification - ";

    public static final int MSG_WHAT_ACTIVE = 100;
    public static final String BUNDLE_KEY_MISSING_NOTIFICATIONS = "missingNotifications";

    public static final String ACTION_NOTIFICATION_LISTENER = "android.service.notification" +
            ".NotificationListenerService";
    public static final String ACTION_NOTIFICATION_PROXY = "android.intent.action.NOTIFICATION_PROXY_SERVICE";

    public static final String ACTION_LAUNCHER_CONTROLLER_PROXY = "android.intent.action.LAUNCHER_CONTROLLER_PROXY_SERVICE";

    public static final String ACTION_LAUNCHER_CONTROLLER_SECNE_CARD_PROXY = "android.intent.action.SCENE_CARD_PROXY_SERVICE";



    private RemoteCallbackList<INotificationListener> mCallbackList;


    private Handler mHandler;
    private final AtomicBoolean mIsProxyBind = new AtomicBoolean(false);
    private final IBinder mLauncherControllerService = new LauncherControllerProxy();
    //private final IBinder mLCService = new EASFrameworkSupportService();
    private final IBinder mLCSceneCardService = new SceneCardProxy();


    @Override
    public void onCreate() {
        super.onCreate();
        HandlerThread handlerThread = new HandlerThread("active_notification_thread");
        handlerThread.start();
        mHandler = new Handler(handlerThread.getLooper());
    }

    @Override
    public void onTrimMemory(int level) {
        super.onTrimMemory(level);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        if (ACTION_NOTIFICATION_LISTENER.equals(intent.getAction())) {
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
                return super.onBind(intent);
            } else {
                IBinder iBinder = super.onBind(intent);
                updateMissingNotifications();
                return iBinder;
            }
        } else if (ACTION_NOTIFICATION_PROXY.equals(intent.getAction())) {
            mIsProxyBind.set(true);
            mCallbackList = new RemoteCallbackList<>();
            NotificationCenterData.getInstance().setRemoteCallback(mCallbackList);
            return new ProxyServiceImpl();
        } else if (ACTION_LAUNCHER_CONTROLLER_PROXY.equals(intent.getAction())) {
            return mLauncherControllerService;
        } else if (ACTION_LAUNCHER_CONTROLLER_SECNE_CARD_PROXY.endsWith(intent.getAction())) {
            return mLCSceneCardService;
        }
        return null;
    }

    private void updateMissingNotifications() {
        try {
            StatusBarNotification[] activeNotifications = getActiveNotifications();
            if (activeNotifications == null) {
                Log.w(TAG, "updateMissingNotifications: nothing");
                return;
            }
            if (DEBUG) {
                Log.d(TAG, "activeNotifications.count=" + activeNotifications.length);
            }
            Message message = Message.obtain(mHandler, MSG_WHAT_ACTIVE);
            Bundle bundle = new Bundle();
            bundle.putParcelableArray(BUNDLE_KEY_MISSING_NOTIFICATIONS, activeNotifications);
            message.setData(bundle);
            message.sendToTarget();
        } catch (Exception e) {
            Log.e(TAG, "case exception " + e);
        }
    }

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    @Override
    public void onListenerConnected() {
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
            updateMissingNotifications();
        }
        Log.i(TAG, "onListenerConnected");
    }

    @RequiresApi(Build.VERSION_CODES.N)
    @Override
    public void onListenerDisconnected() {
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
            requestRebind(new ComponentName(getApplication(), this.getClass()));
        }
        Log.i(TAG, "onListenerDisconnected");
    }

    @Override
    public void onNotificationPosted(StatusBarNotification sbn) {
        Log.i(TAG, "receive a notification.");
        handleNotification(sbn);
    }

    @RequiresApi(Build.VERSION_CODES.O)
    @Override
    public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap, int reason) {
        if (DEBUG) {
            Log.d(TAG, "onNotificationRemoved: with reason=" + reason);
        }
        if (reason == REASON_PACKAGE_CHANGED) {
            Log.w(TAG, "app uninstall onNotificationRemoved");
            cancelNotification(sbn);
        } else {
            super.onNotificationRemoved(sbn, rankingMap, reason);
        }
    }

    @Override
    public void onNotificationRemoved(StatusBarNotification sbn) {
        cancelNotification(sbn);
    }

    private void cancelNotification(StatusBarNotification sbn) {
        if (sbn == null) {
            return;
        }
        if (DEBUG) {
            Log.d(TAG, "onNotificationRemoved, sbn = " + sbn.getNotification());
        }
        if (ignoreNotification(sbn)) {
            return;
        }
        sendNotifyData(sbn, false);
    }

    /**
     * 处理通知
     *
     * @param sbn 数据
     */
    private void handleNotification(StatusBarNotification sbn) {
        if (ignoreNotification(sbn)) {
            Log.i(TAG, "onNotificationPosted Invalid, sbn = " + sbn);
            return;
        }
        if (DEBUG) {
            Log.d(TAG, "onNotificationPosted success, sbn = " + sbn);
        }
        sendNotifyData(sbn, true);
    }

    /**
     * 通知数据至IPCProxyService
     *
     * @param sbn     数据
     * @param isToAdd true是添加消息,false是移除消息
     */
    private void sendNotifyData(StatusBarNotification sbn, boolean isToAdd) {
        if (isToAdd) {
            postNotification(sbn);
        } else {
            removeNotification(sbn);
        }
    }

    /**
     * 添加数据至NotificationPanelView
     */
    private synchronized void postNotification(StatusBarNotification notificationRecord) {
        // 本地和推送发送通知的地方
        NotificationCenterData.getInstance().enqueueNotificationWithTag(notificationRecord);
    }

    /**
     * 删除数据至NotificationPanelView
     */
    private synchronized void removeNotification(StatusBarNotification notificationRecord) {
        Log.i(TAG, "remove notification " + notificationRecord.getTag());
        NotificationCenterData.getInstance().cancelNotificationWithTag(notificationRecord);
    }


    /**
     * 忽略的消息
     *
     * @param sbn 数据
     * @return 是否成功
     */
    public boolean ignoreNotification(StatusBarNotification sbn) {
        if (sbn == null || sbn.getNotification() == null) {
            return true;
        }

        final String packageName = sbn.getPackageName();
        final Notification notification = sbn.getNotification();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            String category = sbn.getNotification().category;
            if (Notification.CATEGORY_SERVICE.equals(category)
                    || Notification.CATEGORY_SYSTEM.equals(category)
                    || Notification.CATEGORY_CALL.equals(category)
                    || Notification.CATEGORY_PROGRESS.equals(category)) {
                Log.i(TAG, "ignoreNotification packageName = " + packageName + " with category=" + category);
                return true;
            }
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
            if ("ranker_group".equals(notification.getGroup())) {
                return true;
            }
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            final String channelId = notification.getChannelId();
            if (inBlackListOfChannels(channelId)) {
                Log.i(TAG, "ignoreNotification packageName = " + packageName + " with channelId=" + channelId);
                return true;
            }

            if (("com.ecarx.naviwidget").equals(channelId)) {
                return false;
            }
        }

        if (inBlackList(packageName)) {
            Log.i(TAG, "ignoreNotification packageName = " + packageName + " inBlackList=");
            return true;
        }

        final String tag = sbn.getTag();
        if (!TextUtils.isEmpty(tag) && tag.startsWith(CHANNEL_PREFIX)) {
            Log.i(TAG, "ignoreNotification packageName = " + packageName + " tag=" + tag);
            return true;
        }

        if (notification != null && ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0)) {
            //  startForeground调用而来的通知，暂不常驻，也不显示
            Log.i(TAG, "ignoreNotification packageName = " + packageName + " with flags=0x" + Integer.toHexString(notification.flags));
            return true;
        }
        return false;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    /**
     * 通知的黑名单
     *
     * @param packageName 包名
     * @return 是否包含在黑名单里面
     */
    private boolean inBlackList(String packageName) {
        if (TextUtils.isEmpty(packageName)) {
            return true;
        }
        final String[] blackArray = getResources().getStringArray(R.array.notify_black_list);
        final HashSet<String> blackListSet = new HashSet<>(Arrays.asList(blackArray));
        return blackListSet.contains(packageName);
    }

    /**
     * Channel屏蔽
     *
     * @param channelId 通道id
     * @return 是否包含屏蔽的包
     */
    @RequiresApi(api = Build.VERSION_CODES.O)
    private boolean inBlackListOfChannels(String channelId) {
        if (TextUtils.isEmpty(channelId)) {
            return true;
        }
        final String[] blackArray = getResources().getStringArray(R.array.channel_black_list);
        final HashSet<String> blackListSet = new HashSet<>(Arrays.asList(blackArray));
        return blackListSet.contains(channelId);
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.d(TAG, "onUnbind: ");
        return super.onUnbind(intent);
    }

    /**
     * 注册通知的监听
     *
     * @param callback 回调给服务端的数据
     */
    private synchronized void register(INotificationListener callback) {
        if (mCallbackList == null) {
            mCallbackList = new RemoteCallbackList<>();
        }
        mCallbackList.register(callback);
    }

    /**
     * 解注册通知的监听
     * <p> 解注册 <br>
     *
     * @param callback {@link INotificationListener}
     * @return 解注册是否成功
     * @since 1.0.0
     */
    private synchronized boolean unregister(INotificationListener callback) {
        return mCallbackList.unregister(callback);
    }


    /**
     * 代理数据接收和数据显示的跨进程处理类
     * 设置数据回调给NotificationPanelView
     */
    public class ProxyServiceImpl extends INotificationService.Stub {

        private static final String TAG = "ProxyServiceImpl";

        @Override
        public void addNotificationListener(INotificationListener callback) {
            register(callback);
            Log.i(TAG, "[" + AppUtils.getProcessName(ECarxNotificationListenerService.this,
                    Process.myPid()) + " ] addNotificationListener");
        }

        @Override
        public void removeNotificationListener(INotificationListener callback) {
            boolean unregister = unregister(callback);
            Log.i(TAG, "removeNotificationListener: " + unregister);
        }

        @Override
        public void cancelAllNotifications() {
            Log.i(TAG, "cancelAllNotifications() called");
            ECarxNotificationListenerService.this.cancelAllNotifications();
        }

        @Override
        public void cancelNotification(String key) {
            Log.i(TAG, "cancelNotification() called with: key = [" + key + "]");
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                ECarxNotificationListenerService.this.cancelNotification(key);
            } else {
                String[] keys = key.split("\\|");
                ECarxNotificationListenerService.this.cancelNotification(keys[1], keys[3], Integer.parseInt(keys[2]));
            }
        }
    }
}
